import { MongodbClient } from "../lib/db.client.mongodb";
import { PgClient } from "../lib/db.client.pg";

import {
  Book,
  Contact,
  ContactGroup,
  ENUM_PASTER,
  ENUM_VOUCHER,
  Paster,
  User,
  Voucher,
  VoucherGroup,
  ENUM_BOOK_DIRECTION as ENUM_BOOK_DIRECTION_NEW,
  ConditionList,
} from "qqlx-core";
import {
  BookSchema,
  toDivideSafe,
  PasterSchema,
  VoucherSchema,
  ContactSchema,
  ContactGroupSchema,
  getVoucherBookType,
  VoucherGroupSchema,
  getConditionMatchStr,
  getConditionMatchEnum,
  getConditionMatchStrOrs,
  getConditionMatchInteger,
} from "qqlx-cdk";

import { User as OldUser } from "../../../qqlx.old/qqlx-core";
import { Corp as OldCorp } from "../../../qqlx.old/qqlx-core";
import { Contact as OldContact } from "../../../qqlx.old/qqlx-core";
import { ENUM_BOOK_DIRECTION, ENUM_BOOK_TYPE, Book as OldBook } from "../../../qqlx.old/qqlx-core";

import { VoucherDao, VoucherGroupDao } from "../dao/river";
import { doBashPrint } from "../lib/log";
import { getTradeId } from "../lib/crypto";
import { ObjectId } from "mongodb";
import { PasterDao } from "../dao/pond";
import { ContactDao, ContactGroupDao } from "../dao/pond";
import { BookDao } from "../dao/pond";

export async function doVoucherInsert(
  mongo_master_corp_id: OldCorp["_id"],
  _id_to_user_map: Map<OldUser["_id"], User>,
  _id_to_contact_map: Map<OldContact["_id"], Contact>,
) {
  const mongodbClient = new MongodbClient("mongodb://localhost:27017/qqlx");
  const pgClient = new PgClient("127.0.0.1", "5432", "production", "postgres", "postgres");

  console.group("\n开始迁移mongo财务记录数据");
  const stat = { before: 0, wanner: 0, now: 0 };

  /** 能够根据旧财务记录，查询到新财务记录 */
  const _id_to_voucher_map = new Map<OldBook["_id"], Voucher>();

  try {
    await mongodbClient.start();
    const pgClientIns = await pgClient.start();

    const colBooks = mongodbClient.getCollection("books");
    const colCorps = mongodbClient.getCollection("corps");
    const corp = (await colCorps.findOne<OldCorp>({ _id: mongo_master_corp_id as unknown as ObjectId })) as OldCorp;
    const corpQuery = { corpId: corp._id as unknown as ObjectId, isDisabled: false };

    // keyOrigin 客户公司 keyHouse 我方银行（有几条记录是空的）
    const SaleHKQuery = { type: ENUM_BOOK_TYPE.YSZK, direction: ENUM_BOOK_DIRECTION.DAI };
    const SaleHKBookLs = await colBooks.find<OldBook>({ ...corpQuery, ...SaleHKQuery }).toArray();

    // keyOrigin 客户公司 keyHouse 我方公司名称
    const SaleFPQuery = { type: ENUM_BOOK_TYPE.YSZK_VAT, direction: ENUM_BOOK_DIRECTION.JIE };
    const SaleFPBookLs = await colBooks.find<OldBook>({ ...corpQuery, ...SaleFPQuery }).toArray();

    // keyOrigin 供应商公司 keyHouse 我方银行
    const PurchaseHKQuery = { type: ENUM_BOOK_TYPE.YFZK, direction: ENUM_BOOK_DIRECTION.JIE };
    const PurchaseHKBookLs = await colBooks.find<OldBook>({ ...corpQuery, ...PurchaseHKQuery }).toArray();

    // keyOrigin 供应商公司 keyHouse 我方公司名称
    const PurchaseFPQuery = { type: ENUM_BOOK_TYPE.YFZK_VAT, direction: ENUM_BOOK_DIRECTION.DAI };
    const PurchaseFPBookLs = await colBooks.find<OldBook>({ ...corpQuery, ...PurchaseFPQuery }).toArray();

    const mongo_book_list = [...SaleHKBookLs, ...SaleFPBookLs, ...PurchaseHKBookLs, ...PurchaseFPBookLs];
    console.log("旧数据（销售/采购/货款/发票）", mongo_book_list.length);
    stat.wanner = mongo_book_list.length;

    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 缓存唯一的财务记录数据包 / 财务联系人数据包

    const bookDao = new BookDao(pgClientIns.getRepository<Book>(BookSchema));
    const pasterDao = new PasterDao(pgClientIns.getRepository<Paster>(PasterSchema));
    const contactGroupDao = new ContactGroupDao(pgClientIns.getRepository<ContactGroup>(ContactGroupSchema));
    const contactDao = new ContactDao(pgClientIns.getRepository<Contact>(ContactSchema));

    const voucherGroupDao = new VoucherGroupDao(pgClientIns.getRepository<VoucherGroup>(VoucherGroupSchema));
    const voucherDao = new VoucherDao(pgClientIns.getRepository<Voucher>(VoucherSchema));

    const master = _id_to_user_map.get(corp.userId) as User;
    const master_condis = [getConditionMatchStr("refOwnerUUid32", master.uuid32)];

    // 缓存唯一的财务记录数据包
    const only_voucher_group = {} as { value: VoucherGroup };
    const tonOrderGroupCount = await voucherGroupDao.count(master_condis);
    if (tonOrderGroupCount === 0) {
      const schema = getDefaultGroup(master.uuid32);
      only_voucher_group.value = await voucherGroupDao.insertOne(schema);
    } else {
      const { firstMatch } = await voucherGroupDao.queryMatch(master_condis);
      if (!firstMatch) throw { message: `数据包异常：${master.nickname}` };
      only_voucher_group.value = firstMatch;
    }

    // 缓存唯一的联系人数据包
    const only_contact_group = {} as { value: ContactGroup };
    const contact_group_count = await contactGroupDao.count(master_condis);
    if (contact_group_count <= 3) {
      const contactGroupSchema = new ContactGroupSchema();
      contactGroupSchema.refOwnerUUid32 = master.uuid32;
      contactGroupSchema.name = "财务往来";
      contactGroupSchema.remark = "由<翡翠财务>生成";
      only_contact_group.value = await contactGroupDao.insertOne(contactGroupSchema);
    } else {
      const { firstMatch } = await contactGroupDao.queryMatch(master_condis);
      if (!firstMatch) throw { message: `数据包异常：${master.nickname}` };
      only_contact_group.value = firstMatch;
    }

    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 将历史明细账的所有对方单位，写入通讯录

    // 目前所有联系人，为了防止名称重复
    const contactNameMap = new Map<string, Contact>();
    for (const [_, contact] of _id_to_contact_map) {
      contactNameMap.set(contact.name, contact);
    }

    const only_group_contact_count = await contactDao.count([
      getConditionMatchStr("refOwnerUUid32", master.uuid32),
      getConditionMatchInteger("refContactGroupId", only_contact_group.value.id),
    ]);
    if (only_group_contact_count === 0) {
      const contactNames = Array.from(new Set(mongo_book_list.map((b) => b.keyOrigin))).filter(Boolean);
      const contactSchemaLs: Contact[] = [];
      for (const mongo_book_opside_name of contactNames) {
        const contact = contactNameMap.get(mongo_book_opside_name); // 和目前所有人对比，为了防止名称重复
        if (contact) continue;

        const schema = new ContactSchema();
        schema.refOwnerUUid32 = master.uuid32;
        schema.refContactGroupId = only_contact_group.value.id;
        schema.name = mongo_book_opside_name;
        schema.remark = "由<翡翠财务>生成";
        contactSchemaLs.push(schema);
      }

      const batchCount = 2000;
      while (contactSchemaLs.length > 0) {
        const part = contactSchemaLs.splice(0, batchCount);
        const ls = await contactDao.insertManyAndQuery(part);
        ls.map((d) => contactNameMap.set(d.name, d));
        doBashPrint(`第（1/5）步正在新增财务联系人（${batchCount}）剩余${contactSchemaLs.length}`);
      }
    }

    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 整理银行数据

    const qituo1 = "奇拓泗泾";
    const qituo2 = "奇拓小昆山";
    const qituo3 = "奇拓泰隆";
    const zhuqi1 = "著祺农行";
    const zhuqi2 = "著祺泰隆";
    const wx = "微信";
    const zfb = "支付宝";
    const gr = "个人银行卡";
    const bankNames = [qituo1, qituo2, qituo3, zhuqi1, zhuqi2, wx, zfb, gr];

    const bankLabelMap = new Map<string, string>();
    bankLabelMap.set("泗", qituo1);
    bankLabelMap.set("泾", qituo1);
    bankLabelMap.set("泗泾", qituo1);
    bankLabelMap.set("昆", qituo2);
    bankLabelMap.set("昆山", qituo2);
    bankLabelMap.set("小昆山", qituo2);
    bankLabelMap.set("泰", qituo3);
    bankLabelMap.set("泰隆", qituo3);
    bankLabelMap.set("奇拓", qituo3);
    bankLabelMap.set("著", zhuqi1);
    bankLabelMap.set("著祺", zhuqi1); // 一个点
    bankLabelMap.set("著褀", zhuqi1); // 两个点
    bankLabelMap.set("著泰隆", zhuqi2);
    bankLabelMap.set("微信", wx);
    bankLabelMap.set("微+支", wx);
    bankLabelMap.set("wx", wx);
    bankLabelMap.set("WX", wx);
    bankLabelMap.set("个人", gr);
    bankLabelMap.set("魏卡", gr);
    bankLabelMap.set("农行", gr);

    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

    const pgCount = await voucherDao.count(master_condis);
    stat.before = pgCount;

    if (pgCount === 0) {
      const voucher_hk_schema_list: Voucher[] = [...SaleHKBookLs, ...PurchaseHKBookLs].map((HK: OldBook) => {
        const schema = getVoucherFromBook(HK, master.uuid32);
        schema.refVoucherGroupId = only_voucher_group.value.id;

        if (HK.type === ENUM_BOOK_TYPE.YSZK) schema.type = ENUM_VOUCHER.INCOME_HUOKUAN;
        else if (HK.type === ENUM_BOOK_TYPE.YFZK) schema.type = ENUM_VOUCHER.EXPENSE_HUOKUAN;
        else throw { message: `历史财务/货款类型异常` };

        const contactCache = contactNameMap.get(HK.keyOrigin) || contactNameMap.get(`未知联系人`);
        if (contactCache?.refOwnerUUid32 !== master.uuid32) throw { message: `联系人异常：${HK.code}` };

        schema.tradeId = getTradeId();
        schema.tradeTime = HK.timeCreate.toString();
        schema.tradeYear = new Date(HK.timeCreate).getFullYear();
        schema.tradeMonth = new Date(HK.timeCreate).getMonth() + 1;
        schema.tradeScope = `${master.uuid32}-${only_voucher_group.value.id}`;
        schema.refCreatorId = master.uuid32;
        schema.refContactId = contactCache!.id; // 财务七要素

        schema.contractSide = HK.keyHouse;
        schema.contractSideOp = contactCache.name;
        schema.contractTime = schema.timeCreate.toString();
        schema.labelStr = [HK.keyHouse, bankLabelMap.get(HK.keyHouse)].filter(Boolean).join("；");
        schema.content = HK._id; // 存放财务记录历史_id
        return schema;
      });

      const voucher_fp_schema_list: Voucher[] = [...SaleFPBookLs, ...PurchaseFPBookLs].map((FP) => {
        const schema = getVoucherFromBook(FP, master.uuid32);
        schema.refVoucherGroupId = only_voucher_group.value.id;

        if (FP.type === ENUM_BOOK_TYPE.YSZK_VAT) schema.type = ENUM_VOUCHER.INCOME_FAPIAO;
        else if (FP.type === ENUM_BOOK_TYPE.YFZK_VAT) schema.type = ENUM_VOUCHER.EXPENSE_FAPIAO;
        else throw { message: `历史财务/发票类型异常` };

        const contactCache = contactNameMap.get(FP.keyOrigin) || contactNameMap.get(`未知联系人`);
        if (contactCache?.refOwnerUUid32 !== master.uuid32) throw { message: `联系人异常：${FP.code}` };

        schema.tradeId = getTradeId();
        schema.tradeTime = FP.timeCreate.toString();
        schema.tradeYear = new Date(FP.timeCreate).getFullYear();
        schema.tradeMonth = new Date(FP.timeCreate).getMonth() + 1;
        schema.tradeScope = `${master.uuid32}-${only_voucher_group.value.id}`;
        schema.refContactId = contactCache!.id;
        schema.refCreatorId = master.uuid32;

        schema.contractSideOp = FP.keyOrigin; // 对方公司名称
        schema.contractSide = FP.keyHouse; // 我方公司名称
        schema.contractTime = schema.timeCreate.toString();
        schema.content = FP._id; // 存放财务记录历史_id
        return schema;
      });

      const batchCount = 2000;
      const schemaAllLs = [...voucher_hk_schema_list, ...voucher_fp_schema_list];
      const voucherCreatedLs: Voucher[] = [];
      while (schemaAllLs.length > 0) {
        const part = schemaAllLs.splice(0, batchCount);
        const ls = await voucherDao.insertManyAndQuery(part);
        voucherCreatedLs.push(...ls);
        doBashPrint(`第（2/5）步正在推送（货款/发票）财务记录（${batchCount}）剩余${schemaAllLs.length}`);
      }

      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 缓存新财务记录

      const oids = mongo_book_list.map((c) => c._id);
      const condis3 = [...master_condis, getConditionMatchStrOrs("content", oids)];
      const voucherAll = await voucherDao.queryAll(condis3);
      voucherAll.list.map((exist) => _id_to_voucher_map.set(exist.content, exist));

      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 根据货款信息生成银行标签（Paster）

      const bankPasterLs = bankNames.map((n) => ({ ...getPasterSchema(master.uuid32, n), type: ENUM_PASTER.FUND_ACCOUNT }));
      const bankEntiies = await pasterDao.insertManyAndQuery(bankPasterLs);

      const masterBankNameMap = new Map<string, Paster>();
      bankEntiies.map((d) => masterBankNameMap.set(d.name, d));

      const bankDefault = await pasterDao.insertOne({
        ...getPasterSchema(master.uuid32, "其他资金渠道"),
        type: ENUM_PASTER.FUND_ACCOUNT,
      });
      masterBankNameMap.set("其他资金渠道", bankDefault);

      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 根据发票生成公司标签（Paster）

      const masterCorpNameMap = new Map<string, Paster>();
      const pasterLs = await pasterDao.queryAll([...master_condis, getConditionMatchEnum("type", ENUM_PASTER.CORP)]);
      pasterLs.list.forEach((d) => {
        masterCorpNameMap.set(d.name, d);
      });

      const FPBookLs = [...SaleFPBookLs, ...PurchaseFPBookLs];
      const pasterCorpSchemaLs: Paster[] = [];
      for (const name of Array.from(new Set(FPBookLs.map((b) => b.keyHouse))).filter((d) => !!d)) {
        const _name = name.replace("褀", "祺").replace("上海宝珍企业发展有限公司", "上海奇拓金属材料有限公司");
        if (masterCorpNameMap.get(_name)) continue;

        const schema = getPasterSchema(master.uuid32, _name);
        schema.type = ENUM_PASTER.CORP;
        pasterCorpSchemaLs.push(schema);
      }

      while (pasterCorpSchemaLs.length > 0) {
        const part = pasterCorpSchemaLs.splice(0, batchCount);
        const ls = await pasterDao.insertManyAndQuery(part);
        ls.map((d) => masterCorpNameMap.set(d.name, d));
        doBashPrint(`第（3/5）步正在推送标签 From 发票公司（${batchCount}）剩余${pasterCorpSchemaLs.length}`);
      }

      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
      // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 为财务记录记账

      const bookSchemaLs: Book[] = [];
      voucherCreatedLs
        .filter((voucher) => voucher.isDisabled === false)
        .map((voucher) => {
          const base = new BookSchema();

          base.tradeId = voucher.tradeId;
          base.tradeTime = voucher.tradeTime;
          base.tradeYear = voucher.tradeYear;
          base.tradeMonth = voucher.tradeMonth;
          base.tradeScope = voucher.tradeScope;
          base.refCreatorId = voucher.refCreatorId;
          base.refContactId = voucher.refContactId; // 财务七要素

          //
          base.refOwnerUUid32 = master.uuid32;
          base.coupleId = getTradeId();
          base.amount = toDivideSafe(voucher.amount, 100, 2);

          const { typeJIE, typeDAI } = getVoucherBookType(voucher.type);
          const Jie = structuredClone(base);
          Jie.type = typeJIE;
          Jie.direction = ENUM_BOOK_DIRECTION_NEW.JIE;
          bookSchemaLs.push(Jie);

          const Dai = structuredClone(base);
          Dai.type = typeDAI;
          Dai.direction = ENUM_BOOK_DIRECTION_NEW.DAI;
          bookSchemaLs.push(Dai);
        });
      while (bookSchemaLs.length > 0) {
        const part = bookSchemaLs.splice(0, batchCount);
        await bookDao.insertMany(part);
        doBashPrint(`（5/5）正在为财务记录登记明细账（${batchCount}）剩余${bookSchemaLs.length}`);
      }
    }

    // 第二次缓存数据
    const oids = mongo_book_list.map((c) => c._id);
    const voucherAll = await voucherDao.queryAll([...master_condis, getConditionMatchStrOrs("content", oids)]);
    voucherAll.list.map((exist) => _id_to_voucher_map.set(exist.content, exist));

    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░▄▄▄▄▄▄░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░▄█▀▀▀▀▀█▄░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▄█░░▄▄▄░░█▄░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▀▀█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░█░░█▄▄█░░█░░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░▀▄▀▄▄▄▄▄▀▄▀░░░░░░░░░░░░░░░░░░░░░░░░░░
    // ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

    const pgCountFinal = await voucherDao.count(master_condis);
    stat.now = pgCountFinal;

    console.log(`🔴迁移前 >> 迁移后 / river_ton_sku_sale`, stat.before, stat.now);
    console.log(`期望`, stat.wanner);
    console.log(`📉误差`, stat.now - stat.before - stat.wanner);
    console.groupEnd();
    console.log("====================================");
  } finally {
    await pgClient.stop();
    await mongodbClient.stop();
  }

  return { _id_to_voucher_map };
}

function getDefaultGroup(masterId: string) {
  const schema: VoucherGroup = new VoucherGroupSchema();
  schema.refOwnerUUid32 = masterId;
  schema.name = "上海/财务总账";
  schema.sequence = 1;
  return schema;
}

function getVoucherFromBook(old: OldBook, masterId: User["uuid32"]) {
  const schema: Voucher = new VoucherSchema();
  schema.timeCreate = old.timeCreate.toString();
  schema.timeUpdate = old.timeUpdate.toString();
  schema.isDisabled = !!old.isDisabled;

  schema.refOwnerUUid32 = masterId;
  schema.remark = old.remark;
  schema.code = old.keyCode || old.code;

  schema.amount = toDivideSafe(old.amount, 100, 2);
  return schema;
}

function getPasterSchema(masterId: User["uuid32"], name: string) {
  const schema = new PasterSchema();
  schema.refOwnerUUid32 = masterId;
  schema.name = name;
  schema.remark = "由<翡翠财务>生成";
  return schema;
}
